package edu.siu.math.egut.main;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.BadLiECommandException;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.io.ParabolicException;
import edu.siu.math.egut.io.ParabolicPieceException;
import edu.siu.math.egut.io.SunipotentFormatException;
import edu.siu.math.egut.util.ActionFailureException;
import edu.siu.math.egut.util.BadSubstitutionException;
import edu.siu.math.egut.util.Group;
import edu.siu.math.egut.util.NoSuchElementException;
import edu.siu.math.egut.util.NotARootException;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Sunipotent;
import edu.siu.math.egut.util.Unipotent;
import edu.siu.math.egut.util.WeylWord;
import edu.siu.math.egut.util.WeylWordReadException;

/**
 * @author Joseph Hundley
 * 
 */
public class Action {

    private static final String INSTRUCTIONS = "This program requires at least three arguments:\n"
	    + "(1) the name of an exceptional group\n"
	    + "(2) a proper parabolic subgroup, specified by naming the simple roots in the unipotent radical\n"
	    + "(3) a piece of that unipotent radical, specified either by\n"
	    + "\t(a) giving an integral weight for each root named at step (2), or\n"
	    + "\t(b) simply giving a single integer,\n"
	    + "EXAMPLE: F4 4 2\n";
    static final String NO_ARGS_INSTRUCTIONS = INSTRUCTIONS+"Please input the name of a file where " +
    		"input of this type can be found.";
    private static final String FOURTH_ARG_NOT_A_POLYNOMIAL_MESSAGE = "In this program, when four or " +
    		"more arguments are passed,\n the fourth must be a polynomial.  Your fourth argument could \n" +
    		"not be parsed as a Polynomial.  Please try again.";

    /**
     * Let P =MU be a parabolic subgroup of a reductive group G. Let {i1 < i2 <
     * ... < ik}. Let r be the semisimple rank of G and identify roots of G with
     * r-tuples of integers by writing them in terms of the basis of simple
     * roots. Define a mapping Z^r -> Z^k by (n[1], ... n[r]) ->( n[i1], ...,
     * n[ik]). Each of the fibers of this map defines an M-module. A realization
     * of G determines a basis for each of these M-modules. The purpose of this
     * program is to compute the matrices of elements of M with respect to these
     * bases.
     * 
     * Syntax: CharActn2 Group Parabolic PieceSpecifier Character Elements
     * Example: CharActn2 F4 4 2 optionally: u1232 x_{0110}(r) x_{-0010}(s)
     * 
     * @param args
     *            need to specify the group G, parabolic P, and which piece we
     *            consider, and finally an element of M.
     */
    public static void main(String[] args) {

	if( args.length == 0 ){
	    try {
		System.out.println(NO_ARGS_INSTRUCTIONS);
		args=Algorithms.getCommandsFromFile(
			Algorithms.getFileNameNoBail(new Scanner(System.in)));
	    } catch (FileNotFoundException e) {
		e.printStackTrace();
	    }
	}
	if (args.length == 1) {
	    args = Algorithms.getInput(args);
	}

	if (args.length == 2) {
	    System.out.println(INSTRUCTIONS);
	    System.exit(1);
	}


	// extract input from args
	// args[0] is supposed to be the name of a group. Check that it is one.
	final Group g = Group.getGroup(args[0]);
	if (g == null) {
	    System.out.println("First argument --" + args[0]
		    + "-- is not the name of an exceptional group.");
	    System.exit(1);
	}
	try {
	    /*
	     * args[1] should specify a parabolic subgroup of that exceptional
	     * group. therefore, (1) every character in it should be a digit
	     * between 1 and the rank of g inclusive, and (2) they should all be
	     * different. Those conditions being satisfied, the interpretation
	     * is that you are listing those simple roots such that the
	     * corresponding root subgroup is in the unipotent radical.
	     */

	    final String parabolicSpecifier = parseParabolic(args[1], g);

	    /*
	     * args[2] should specify a piece. Can do it in one of two ways (a)
	     * an integer for each root listed in args[1] (b) a single integer.
	     */

	    final String pieceSpecifier = parsePiece(args[2], args[1].length());

	    /*
	     * get list of roots in the specified "piece group" from LiE
	     */
	    final LiE lie = new LiE();
	    
	    final int[][] pieceRoots = Algorithms.readLiEMatrix(lie.exec(
		    "pieceGroup(" + pieceSpecifier + "," + parabolicSpecifier
			    + "," + args[0] + ")", OutputPref.SHOW));

	    Polynomial ell = Polynomial.create(0);
	    Polynomial originalEll= ell;
	    if (args.length > 3) 
		try {
		    ell = Polynomial.read(args[3]);
		    originalEll= ell;
		    System.out.println(args[3]);
		    if(args.length >4){
			/*Remaining input is interpreted as group elements that should 
			 * act on our set up.  Since this is a (left) action, we work
			 * from right to left.
			 */
			for (int i = args.length-1; i >3; i--)
			    ell =nonInteractiveAction(args[i], ell, g, pieceRoots);
		    }

		} catch (PolynomialReadException e) {
		    System.out.println(FOURTH_ARG_NOT_A_POLYNOMIAL_MESSAGE);
		    ell = Algorithms.getPoly(new Scanner(System.in));
		    originalEll= ell;

		} catch (ActionFailureException e) {
		    
		    e.printStackTrace();
		} catch (WeylWordReadException e) {
		    
		    e.printStackTrace();
		}

	    interActiveAction(ell, g, pieceRoots, originalEll);

	} catch (final ParabolicException e) {
	    
	    System.out
		    .println("Second argument -- "
			    + args[1]
			    + "-- could not be parsed as a parabolic in "
			    + args[0]
			    + ".\n"
			    + "required format is to list the simple roots that are in the unipotent radical,\n"
			    + "all mashed together.  Example: 134");
	    e.printStackTrace();

	} catch (final ParabolicPieceException e) {
	    e.printStackTrace();
	} catch (final IOException e) {
	    e.printStackTrace();
	} catch (final NotARootException e) {
	    e.printStackTrace();
	} catch (BadLiECommandException e) {
	    e.printStackTrace();
	} catch (ActionFailureException e) {
	    
	    e.printStackTrace();
	} catch (WeylWordReadException e) {
	    
	    e.printStackTrace();
	}

    }

    private static void interActiveAction(Polynomial ell, Group g,
	    int[][] pieceRoots, Polynomial originalEll) throws ActionFailureException, WeylWordReadException {
	Scanner scanner = new Scanner(System.in);
	String command = scanner.next();
	boolean sticky = true;
	while(!Algorithms.QUIT.matcher(command).matches()){
	    if(Pattern.compile("stickyoff",Pattern.CASE_INSENSITIVE).matcher(command).matches()){
		System.out.println("Sticky off.  (Polynomial reverts to current setting after each action.)");
		sticky = false;
	    }
	    else if(Pattern.compile("sticky(on)?",Pattern.CASE_INSENSITIVE).matcher(command).matches()){
		System.out.println("Sticky on. (Result of each action is starting point for next.)");
		sticky = true;
	    }
	    else if(Pattern.compile("reset", Pattern.CASE_INSENSITIVE).matcher(command).matches())
		ell = originalEll;
	    else if(Algorithms.GROUP_ELEMENT.matcher(command).matches()){
		
		
		try {
			
		    if( sticky ){
			ell=nonInteractiveAction(command, ell,g,pieceRoots);
		    }
		    else {
			nonInteractiveAction(command, ell,g,pieceRoots);

		    }
			
		    
		} catch (PolynomialReadException e) {
		    e.printStackTrace();
		} catch (NotARootException e) {
		    e.printStackTrace();
		}
	    }
	    else{
		
		try {
		    ell = Polynomial.read(command);
		    
		} catch (PolynomialReadException e) {
		    System.out.println("Command "+command+" could not be understood.");
		}
	    }
	    command = scanner.next();
	}
	
	
    }

 
    /*
     * Idea here is to check the format of a string which is supposed to specify
     * a piece, and then parse it from a convenient-to-type format into a format
     * for LiE.
     */
    static String parsePiece(final String string,
	    final int ParabolicLength) throws ParabolicPieceException {
	if ((string.length() != 1) && (string.length() != ParabolicLength))
	    throw new ParabolicPieceException();
	final StringBuilder sb = new StringBuilder("[");
	for (int i = 0; i < string.length(); i++) {
	    if (string.charAt(i) < 48 || string.charAt(i) > 57) {
		throw new ParabolicPieceException();
	    }
	    sb.append(string.charAt(i));
	    sb.append(i == string.length() - 1 ? "]" : ",");
	}

	return sb.toString();

    }

    /*
     * The purpose of this method is twofold: (1) to validate user input, and
     * (2) to allow the main program to accept input in the easiest-for-the-user
     * format, yet have the string in the correct format for feeding to LiE.
     */
    static String parseParabolic(final String string, final Group g)
	    throws ParabolicException {

	final StringBuilder sb = new StringBuilder("[");

	for (int i = 0; i < string.length(); i++) {
	    if (string.charAt(i) < 49 || string.charAt(i) > 48 + g.getRank()) {
		throw new ParabolicException();
	    }
	    sb.append(string.charAt(i));
	    sb.append(i == string.length() - 1 ? "]" : ",");
	}

	return sb.toString();
    }

    private static Polynomial nonInteractiveAction(String command,
	    Polynomial ell, Group g, int[][] pieceRoots)
	    throws PolynomialReadException, NotARootException, ActionFailureException, WeylWordReadException {
	

	// set up unipotent u
	final Unipotent u = new Unipotent(g, pieceRoots, "u");

	    try {
		if(Algorithms.SUNIPOTENT_OF_UNSPECIFIED_RANK.matcher(command).matches()){

		    u.actBy(command);
		    
		    
		    ell = u.chOfVars(ell);		    
		 // make changes of variable, keeping track of the effect on ell

		
		
		}
		else if( Algorithms.weylWord(g).matcher(
			command
			).matches()  ){

		    ell= edu.siu.math.egut.util.Algorithms.action_2(WeylWord.read(command, g), ell, u, g); 
		    
		} // end if matches weyl

		

		
	    } catch (final NoSuchElementException e) {
		System.out.println("Argument " + "-- " + command
			+ "-- could not be read as a group element.  Ignored.");

	    } catch (BadSubstitutionException e) {
		e.printStackTrace();
	    }
	   
	
	// output result to user.
	System.out.println(ell);	
	// return ell to main for possible further use
	return ell;

    }
    

    private static String s2(Group g, char c) {
	StringBuilder sb = new StringBuilder("x_{-");
	sb.append(Algorithms.tupleToShortString(
		g.getPositiveRoots()[Algorithms.value(c)-1]
	));
	sb.append("}(-1)");
	return sb.toString();
    }

    private static String s1(Group g, char c) {
	return "x_{"+Algorithms.tupleToShortString(
		g.getPositiveRoots()[Algorithms.value(c)-1]
		)+"}(1)";
    }

}
